﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;

namespace Isuka.Automation
{
    public abstract class EventSink<TIEvent>
        : IDisposable
    {
        IAutomationObject eventSource;
        IConnectionPoint connectionPoint;
        int connectionCookie;

        protected EventSink(IAutomationObject source)
        {
            Contract.Requires(source != null);

            eventSource = source;

            var container = eventSource.Object as IConnectionPointContainer;
            if (container != null)
            {
                var guid = typeof(TIEvent).GUID;
                container.FindConnectionPoint(guid, out connectionPoint);
                if (connectionPoint != null)
                {
                    connectionPoint.Advise(this, out connectionCookie);
                }
            }
        }

        #region Dispose パターン

        /// <summary>
        /// リソースが解放されているかどうかを示します。
        /// </summary>
        int disposed = 0;

        /// <summary>
        /// EventSink で使用されるすべてのリソースを解放します。
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// EventSink によって使用されているアンマネージ リソースを解放し、オプションでマネージ リソースも解放します。
        /// </summary>
        /// <param name="disposing">マネージ リソースとアンマネージ リソースの両方を解放する場合は true。アンマネージ リソースだけを解放する場合は false。</param>
        protected virtual void Dispose(bool disposing)
        {
            if (System.Threading.Interlocked.Exchange(ref disposed, 1) == 0)
            {
                if (disposing)
                {
                    if (connectionCookie != 0)
                    {
                        connectionPoint.Unadvise(connectionCookie);
                        Marshal.ReleaseComObject(connectionPoint);
                        connectionPoint = null;
                        connectionCookie = 0;
                        eventSource = null;
                    }
                }
                // TODO: アンマネージ リソースを解放

                // Disposed フラグをON
                System.Threading.Interlocked.Exchange(ref disposed, 2);
            }
        }

        /// <summary>
        /// リソースが破棄されているかどうかを示す値を取得します。
        /// </summary>
        [System.Diagnostics.Contracts.Pure]
        public virtual bool IsDisposed
        {
            get { return disposed == 2; }
        }

        /// <summary>
        /// リソースが破棄されている場合に、ObjectDisposedException例外を送出します。
        /// </summary>
        [System.Diagnostics.Contracts.Pure]
        protected void VerifyNotDisposed()
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
        }

        ~EventSink()
        {
            Dispose(false);
        }
        #endregion
    }
}
